package org.yourorghere;

import com.sun.opengl.util.Animator;
import java.awt.Frame;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.Stack;
import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLCanvas;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.glu.GLU;



/**
 * FacesGrouping.java <BR>
 * author: Brian Paul (converted to Java by Ron Cemer and Sven Goethel) <P>
 *
 * This version is equal to Brian Paul's version 1.2 1999/10/21
 */
public class FacesGrouping implements GLEventListener, KeyListener {
    private float x = 0.0f, y = 0.0f, z = -8.0f;
    private float rx = 0.0f, ry = 0.0f, rz = 0.0f;
    private float zoomFactor = 0.0f;

    private float[][] s1 = {
{-0.2100f,-0.1400f,0.1519f},
{-0.2000f,-0.1400f,0.1519f},
{-0.1900f,-0.1400f,0.1519f},
{-0.1800f,-0.1400f,0.1519f},
{-0.1700f,-0.1400f,0.1519f},
{-0.2400f,-0.1300f,0.1519f},
{-0.2300f,-0.1300f,0.1519f},
{-0.2200f,-0.1300f,0.1519f},
{-0.1600f,-0.1300f,0.1519f},
{-0.1500f,-0.1300f,0.1519f},
{-0.1400f,-0.1300f,0.1519f},
{-0.2700f,-0.1200f,0.1519f},
{-0.2600f,-0.1200f,0.1519f},
{-0.2500f,-0.1200f,0.1519f},
{-0.1300f,-0.1200f,0.1519f},
{-0.1200f,-0.1200f,0.1519f},
{-0.1100f,-0.1200f,0.1519f},
{-0.2900f,-0.1100f,0.1519f},
{-0.2800f,-0.1100f,0.1519f},
{-0.1000f,-0.1100f,0.1519f},
{-0.3000f,-0.1000f,0.1519f},
{-0.0900f,-0.1000f,0.1519f},
{-0.3100f,-0.0900f,0.1519f},
{-0.0900f,-0.0900f,0.1519f},
{-0.3200f,-0.0800f,0.1519f},
{-0.0900f,-0.0800f,0.1519f},
{-0.0800f,-0.0800f,0.1519f},
{-0.3300f,-0.0700f,0.1519f},
{-0.0900f,-0.0700f,0.1519f},
{-0.3300f,-0.0600f,0.1519f},
{-0.0900f,-0.0600f,0.1519f},
{-0.3400f,-0.0500f,0.1519f},
{-0.1000f,-0.0500f,0.1519f},
{-0.3500f,-0.0400f,0.1519f},
{-0.1000f,-0.0400f,0.1519f},
{-0.3500f,-0.0300f,0.1519f},
{-0.1100f,-0.0300f,0.1519f},
{-0.3600f,-0.0200f,0.1519f},
{-0.1100f,-0.0200f,0.1519f},
{-0.3600f,-0.0100f,0.1519f},
{-0.1200f,-0.0100f,0.1519f},
{-0.3600f,0.0000f,0.1519f},
{-0.1300f,0.0000f,0.1519f},
{-0.3600f,0.0100f,0.1519f},
{-0.1400f,0.0100f,0.1519f},
{-0.3600f,0.0200f,0.1519f},
{-0.1600f,0.0200f,0.1519f},
{-0.1500f,0.0200f,0.1519f},
{-0.3700f,0.0300f,0.1519f},
{-0.1700f,0.0300f,0.1519f},
{-0.3700f,0.0400f,0.1519f},
{-0.2000f,0.0400f,0.1519f},
{-0.1900f,0.0400f,0.1519f},
{-0.1800f,0.0400f,0.1519f},
{-0.3600f,0.0500f,0.1519f},
{-0.2400f,0.0500f,0.1519f},
{-0.2300f,0.0500f,0.1519f},
{-0.2200f,0.0500f,0.1519f},
{-0.2100f,0.0500f,0.1519f},
{-0.3600f,0.0600f,0.1519f},
{-0.2500f,0.0600f,0.1519f},
{-0.2600f,0.0700f,0.1519f},
{-0.3600f,0.0800f,0.1519f},
{-0.2700f,0.0800f,0.1519f},
{-0.2600f,0.0800f,0.1519f},
{-0.3600f,0.0900f,0.1519f},
{-0.2900f,0.0900f,0.1519f},
{-0.2800f,0.0900f,0.1519f},
{-0.3500f,0.1000f,0.1519f},
{-0.3000f,0.1000f,0.1519f},
{-0.3500f,0.1100f,0.1519f},
{-0.3400f,0.1100f,0.1519f},
{-0.3300f,0.1100f,0.1519f},
{-0.3200f,0.1100f,0.1519f},
{-0.3100f,0.1100f,0.1519f}
    };

   private float[][] s2 = {
{-0.1900f,-0.1500f,0.3519f},
{-0.1800f,-0.1500f,0.3519f},
{-0.2300f,-0.1400f,0.3519f},
{-0.2200f,-0.1400f,0.3519f},
{-0.2100f,-0.1400f,0.3519f},
{-0.2000f,-0.1400f,0.3519f},
{-0.1700f,-0.1400f,0.3519f},
{-0.1600f,-0.1400f,0.3519f},
{-0.1500f,-0.1400f,0.3519f},
{-0.1400f,-0.1400f,0.3519f},
{-0.1300f,-0.1400f,0.3519f},
{-0.2600f,-0.1300f,0.3519f},
{-0.2500f,-0.1300f,0.3519f},
{-0.2400f,-0.1300f,0.3519f},
{-0.1200f,-0.1300f,0.3519f},
{-0.1100f,-0.1300f,0.3519f},
{-0.2800f,-0.1200f,0.3519f},
{-0.2700f,-0.1200f,0.3519f},
{-0.1000f,-0.1200f,0.3519f},
{-0.0900f,-0.1200f,0.3519f},
{-0.2900f,-0.1100f,0.3519f},
{-0.0800f,-0.1100f,0.3519f},
{-0.3000f,-0.1000f,0.3519f},
{-0.0700f,-0.1000f,0.3519f},
{-0.3100f,-0.0900f,0.3519f},
{-0.0700f,-0.0900f,0.3519f},
{-0.3100f,-0.0800f,0.3519f},
{-0.0600f,-0.0800f,0.3519f},
{-0.3200f,-0.0700f,0.3519f},
{-0.0600f,-0.0700f,0.3519f},
{-0.3200f,-0.0600f,0.3519f},
{-0.0700f,-0.0600f,0.3519f},
{-0.3300f,-0.0500f,0.3519f},
{-0.0800f,-0.0500f,0.3519f},
{-0.0700f,-0.0500f,0.3519f},
{-0.3400f,-0.0400f,0.3519f},
{-0.0900f,-0.0400f,0.3519f},
{-0.3400f,-0.0300f,0.3519f},
{-0.1000f,-0.0300f,0.3519f},
{-0.3500f,-0.0200f,0.3519f},
{-0.1000f,-0.0200f,0.3519f},
{-0.3500f,-0.0100f,0.3519f},
{-0.1100f,-0.0100f,0.3519f},
{-0.3500f,0.0000f,0.3519f},
{-0.1200f,0.0000f,0.3519f},
{-0.3500f,0.0100f,0.3519f},
{-0.1300f,0.0100f,0.3519f},
{-0.3400f,0.0200f,0.3519f},
{-0.1500f,0.0200f,0.3519f},
{-0.1400f,0.0200f,0.3519f},
{-0.3400f,0.0300f,0.3519f},
{-0.1700f,0.0300f,0.3519f},
{-0.1600f,0.0300f,0.3519f},
{-0.3400f,0.0400f,0.3519f},
{-0.1900f,0.0400f,0.3519f},
{-0.1800f,0.0400f,0.3519f},
{-0.3300f,0.0500f,0.3519f},
{-0.2400f,0.0500f,0.3519f},
{-0.2300f,0.0500f,0.3519f},
{-0.2200f,0.0500f,0.3519f},
{-0.2100f,0.0500f,0.3519f},
{-0.2000f,0.0500f,0.3519f},
{-0.2700f,0.0600f,0.3519f},
{-0.2600f,0.0600f,0.3519f},
{-0.2500f,0.0600f,0.3519f},
{-0.3300f,0.0700f,0.3519f},
{-0.3000f,0.0700f,0.3519f},
{-0.2900f,0.0700f,0.3519f},
{-0.2800f,0.0700f,0.3519f},
{-0.3300f,0.0800f,0.3519f},
{-0.3100f,0.0800f,0.3519f},
{-0.3400f,0.0900f,0.3519f},
{-0.3100f,0.0900f,0.3519f},
{-0.3400f,0.1000f,0.3519f},
{-0.3100f,0.1000f,0.3519f},
{-0.3300f,0.1100f,0.3519f},
{-0.3200f,0.1100f,0.3519f}
    };

   private float[] m1 = {-0.2400f,-0.0300f,6.1519f};
   private float[] m2 = {-0.2200f,-0.0400f,5.1337f};

   private ArrayList<Integer> connList = new ArrayList<Integer>();

    public static void main(String[] args) {
        Frame frame = new Frame("Simple JOGL Application");
        GLCanvas canvas = new GLCanvas();

        canvas.addGLEventListener(new FacesGrouping());
        frame.add(canvas);
        frame.setSize(640, 480);
        final Animator animator = new Animator(canvas);
        frame.addWindowListener(new WindowAdapter() {

            @Override
            public void windowClosing(WindowEvent e) {
                // Run this on another thread than the AWT event queue to
                // make sure the call to Animator.stop() completes before
                // exiting
                new Thread(new Runnable() {

                    public void run() {
                        animator.stop();
                        System.exit(0);
                    }
                }).start();
            }
        });
        // Center frame
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
        animator.start();
        canvas.requestFocusInWindow();
    }

    public void init(GLAutoDrawable drawable) {
        // Use debug pipeline
        // drawable.setGL(new DebugGL(drawable.getGL()));

        GL gl = drawable.getGL();
        System.err.println("INIT GL IS: " + gl.getClass().getName());

        // Enable VSync
        gl.setSwapInterval(1);

        // Setup the drawing area and shading mode
        gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        gl.glShadeModel(GL.GL_SMOOTH); // try setting this to GL_FLAT and see what happens.


        drawable.addKeyListener(this);
        doGrouping();
    }

    public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
        GL gl = drawable.getGL();
        GLU glu = new GLU();

        if (height <= 0) { // avoid a divide by zero error!
        
            height = 1;
        }
        final float h = (float) width / (float) height;
        gl.glViewport(0, 0, width, height);
        gl.glMatrixMode(GL.GL_PROJECTION);
        gl.glLoadIdentity();
        glu.gluPerspective(45.0f, h, 1.0, 1000.0);
        gl.glMatrixMode(GL.GL_MODELVIEW);
        gl.glLoadIdentity();
        
    }

    public void display(GLAutoDrawable drawable) {
        GL gl = drawable.getGL();

        // Clear the drawing area
        gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
        // Reset the current matrix to the "identity"
        gl.glLoadIdentity();

        // Move the "drawing cursor" around
//        gl.glPushMatrix();
        gl.glScalef(zoomFactor, zoomFactor, zoomFactor);
        gl.glTranslatef(x, y, z);
        gl.glRotatef(rx, 1.0f, 0.0f, 0.0f);
        gl.glRotatef(ry, 0.0f, 1.0f, 0.0f);
        gl.glRotatef(rz, 0.0f, 0.0f, 1.0f);


        gl.glPointSize(2.0f);
        gl.glBegin(GL.GL_POINTS);
        gl.glColor3f(1.0f, 0.0f, 0.0f);

        int sizeS1 = s1.length;
        int sizeS2 = s2.length;

        for(int i = 0;i< sizeS1; i++)
            gl.glVertex3fv(s1[i],0);
        for(int i = 0;i< sizeS2; i++)
            gl.glVertex3fv(s2[i],0);
        gl.glEnd();



        gl.glPointSize(1.0f);
        gl.glBegin(GL.GL_LINES);
        gl.glColor3f(1.0f, 1.0f, 0.0f);
        gl.glVertex3f(10.0f, 0.0f, 0.0f); // +x axis
        gl.glVertex3f(-10.0f, 0.0f, 0.0f); // -x axis

        gl.glColor3f(0.0f, 0.0f, 1.0f);
        gl.glVertex3f(0.0f, 10.0f, 0.0f); // +y axis
        gl.glVertex3f(0.0f, -10.0f, 0.0f); // -y axis

        gl.glColor3f(0.0f, 1.0f, 1.0f);
        gl.glVertex3f(0.0f, 0.0f, 10.0f); // +y axis
        gl.glVertex3f(0.0f, 0.0f, -10.0f); // -y axis


        int i=0,j=0;
        float[][] inner = {};
        float[][] outer = {};
        if( s1.length < s2.length){
            inner = s1;
            outer = s2;
        }

        else
        {
            inner = s2;
            outer = s1;
        }

        int innerSize = inner.length;
        int outerSize = outer.length;


        gl.glBegin(GL.GL_LINES);
        gl.glColor3f(0.0f, 1.0f, 0.0f);
        //while(!connList.isEmpty()){
        int size = connList.size();
        //size = 20;
        for(int k=0;k<size;k+=2){

            gl.glVertex3fv(inner[connList.get(k+1) % innerSize],0);
            gl.glVertex3fv(outer[connList.get(k) % outerSize],0);
        }

        gl.glEnd();



//        gl.glPopMatrix();

        // Flush all drawing operations to the graphics card
        gl.glFlush();
    }



    public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {
    }



    private void doGrouping()
    {
        float[][] inner = {};
        float[][] outer = {};
        float[] m = {};


        if(s1.length < s2.length)
        {
            inner = s1;
            outer = s2;
            m = m1;
        }
        else
        {
            inner = s2;
            outer = s1;
            m = m2;
        }

        int inSize = inner.length;
        int outSize = outer.length;
        
        float slope =0.0f;
        float[] mid = {};
        float[][] bounds = new float[inSize][3];
        float x1,x2,y1,y2,Y,X;

        //calculate middle of each point pair
        for(int i=0; i< inSize; i++)
        {
            mid  = getMidOf(inner[ i ],inner[ (i+1) % inSize] );

            x1 = m[0];
            y1 = m[1];

            x2 = mid[0];
            y2 = mid[1];

            X = x1 * 2.0f;
            Y = (((y2-y1)*(X-x2))/(x2-x1))+ y2;

            bounds[i][0] =  X;
            bounds[i][1] =  Y;

        }

        //Point check
        float[] point = {};
        float[] candidatePoint = {};
        float[] bound1 = {};
        float[] bound2 = {};
        for(int i=0; i<inSize; i++)
        {
            point = inner[i];
            bound1 = bounds[i];
            bound2 = bounds[(i+1)% inSize];
            
            //Counter clockwise search
            for(int j=0; j<outSize ;j++)
            {
                candidatePoint = outer[j];
                if(isInTriangle(bound1,m, bound2, candidatePoint))
                {
                    connList.add(j);
                    connList.add(i);
                    //System.out.println("i:" + i + " j:" + j);
                }
            }

        }
    }

    private float areaCalc(float[] a, float[] b, float[] c) {
        float x1y2 = a[0] * b[1];
        float x2y3 = b[0] * c[1];
        float x3y1 = c[0] * a[1];
        float x2y1 = b[0] * a[1];
        float x3y2 = c[0] * b[1];
        float x1y3 = a[0] * c[1];
        float area = (x1y2 + x2y3 + x3y1 - x2y1 - x3y2 - x1y3) / 2.0f;
        return area;
    }

    private float[] getMidOf(float[] p1, float[] p2)
    {
        float[] mid = new float[3];

        mid[0] = (p2[0] + p1[0]) / 2;
        mid[1] = (p2[1] + p1[1]) / 2;
        return mid;
    }

    private boolean isInTriangle(float[] a, float[] b, float[] c, float[] p)
    {
        float Area = areaCalc(a, b, c);
        float aAlfa = areaCalc(a, b, p);
        float aBeta = areaCalc(c, a, p);
        float aGamma = areaCalc(b, c, p);
        float Alfa = aAlfa/Area;
        float Beta = aBeta/Area;
        float Gamma = aGamma/Area;

        return (Alfa > 0)&(Beta>0)&(Gamma>0);
    }


    public void keyTyped(KeyEvent e) {

    }

    public void keyPressed(KeyEvent e) {
        if (e.isControlDown() && (e.getKeyCode() == KeyEvent.VK_NUMPAD6)) {
            rx += 10;
            return;
        }
        if (e.isControlDown() && (e.getKeyCode() == KeyEvent.VK_NUMPAD4)) {
            rx -= 10;
            return;
        }
        if (e.isControlDown() && (e.getKeyCode() == KeyEvent.VK_NUMPAD8)) {
            ry += 10;
            return;
        }
        if (e.isControlDown() && (e.getKeyCode() == KeyEvent.VK_NUMPAD2)) {
            ry -= 10;
            return;
        }
        if (e.isControlDown() && (e.getKeyCode() == KeyEvent.VK_ADD)) {
            rz += 10;
            return;
        }
        if (e.isControlDown() && (e.getKeyCode() == KeyEvent.VK_SUBTRACT)) {
            rz -= 10;
            return;
        }


        if (e.getKeyCode() == KeyEvent.VK_NUMPAD6) {
            x += 0.1f;
            return;
        }
        if (e.getKeyCode() == KeyEvent.VK_NUMPAD4) {
            x -= 0.1f;
            return;
        }
        if (e.getKeyCode() == KeyEvent.VK_NUMPAD8) {
            y += 0.1f;
            return;
        }
        if (e.getKeyCode() == KeyEvent.VK_NUMPAD2) {
            y -= 0.1f;
            return;
        }
        if (e.getKeyCode() == KeyEvent.VK_ADD) {
            z += 0.1f;
            return;
        }
        if (e.getKeyCode() == KeyEvent.VK_SUBTRACT) {
            z -= 0.1f;
            return;
        }
        if (e.getKeyCode() == KeyEvent.VK_NUMPAD5) {
            x=0.0f; y=0.0f; z=-8.0f;
            rx=0.0f; ry=rz=0.0f;
            return;
        }

        if(e.getKeyCode() == KeyEvent.VK_1)
            zoomFactor += 0.5f;
        if(e.getKeyCode() == KeyEvent.VK_2)
            zoomFactor -= 0.5f;

    }

    public void keyReleased(KeyEvent e) {

    }
}

